package cn.colins.raft.rpc;


import cn.colins.raft.ResourceLifeCycle;
import cn.colins.raft.rpc.handler.RpcClientHandlerInit;
import cn.colins.raft.rpc.handler.RpcServerHandlerInit;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;


public class RpcServer implements ResourceLifeCycle {
    private final static Logger log = LoggerFactory.getLogger(RpcServer.class);

    private NioEventLoopGroup bossGroup = null;
    private NioEventLoopGroup workerGroup = null;

    private final int port;

    private final ChannelInitializer channelInitializer;

    private ChannelFuture future;

    public RpcServer(int port) {
        this.port = port;
        this.channelInitializer = new RpcServerHandlerInit();
    }

    @Override
    public void init() {
        try {
            nettyServerStart();
            waitNettyServerClose();
        }catch (InterruptedException e){
            log.info("关闭 Server 成功,端口：{}", port);
            destroy();
        }
    }

    @Override
    public void destroy() {
        // 优雅的关闭 释放资源
        if (bossGroup != null) {
            bossGroup.shutdownGracefully();
        }
        if (bossGroup != null) {
            bossGroup.shutdownGracefully();
        }
    }

    private final boolean nettyServerStart() throws InterruptedException {
        bossGroup = new NioEventLoopGroup(2);
        workerGroup = new NioEventLoopGroup(16);
        // 服务端启动类
        ServerBootstrap bootstrap = new ServerBootstrap();
        // 传入两个线程组
        bootstrap.group(bossGroup, workerGroup)
                // 指定Channel 和NIO一样是采用Channel通道的方式通信 所以需要指定服务端通道
                .channel(NioServerSocketChannel.class)
                //使用指定的端口设置套接字地址
                .localAddress(new InetSocketAddress(port))
                //服务端可连接队列数,对应TCP/IP协议listen函数中backlog参数
                .option(ChannelOption.SO_BACKLOG, 1024)
                //设置数据处理器
                .childHandler(channelInitializer);
        // 同步等待成功
        future = bootstrap.bind().sync();
        if (future.isSuccess()) {
            log.info("启动 Server 成功,端口：{}", port);
            return true;
        }
        return false;
    }

    private final void waitNettyServerClose() throws InterruptedException {
        future.channel().closeFuture().sync();
    }


}
